#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include "errors.h"
#include "parser.h"
#include "parser_unref.h"
#include "redmapobject.h"
#include "redstringobject.h"

#define def(t, r)								t##_t *r; create(t, r)
#define create(t, r)							\
	do											\
	{											\
		r = malloc(sizeof(t##_t));				\
		memset(r, 0, sizeof(t##_t));			\
												\
		ref_any(r);								\
		r->node.type = nt_##t;					\
		r->node.lineno = self->tokenizer->y;	\
		r->node.position = self->tokenizer->x;	\
	} while (0)

#define ensure(k, v, t, r, ...)					do { if (!expect(self, tk_##k, v)) { unref_##t(r); __VA_ARGS__; return NULL; } } while (0)
#define verify(v, e, t, r, ...)					do { if (!((v) = parse_##e(self))) { unref_##t(r); __VA_ARGS__; return NULL; } } while (0)

/*************** helper functions ***************/

static inline char maybe(parser_t *self, int count, ...)
{
	token_t *token = tokenizer_peek(self->tokenizer);

	if (!token)
	{
		errors_throw(e_tokenizer, "Tokenizer error");
		return 0;
	}

	va_list args;
	va_start(args, count);

	for (int i = 0; i < count; i++)
	{
		if (token->token == va_arg(args, token_type_t))
		{
			va_end(args);
			token_release(&token);
			return 1;
		}
	}

	va_end(args);
	token_release(&token);
	return 0;
}

static inline char assume(parser_t *self, token_type_t type, const char *value)
{
	token_t *token = tokenizer_peek(self->tokenizer);

	if (!token)
	{
		errors_throw(e_tokenizer, "Tokenizer error");
		return 0;
	}

	char result = token->token == type && rstring_equals(token->string_value, value);

	token_release(&token);
	return result;
}

static inline char skipif(parser_t *self, token_type_t type, const char *value)
{
	if (!assume(self, type, value))
		return 0;

	token_unref(tokenizer_next(self->tokenizer));
	return 1;
}

static inline char expect(parser_t *self, token_type_t type, const char *value)
{
	token_t *token = tokenizer_next(self->tokenizer);

	if (!token)
	{
		errors_throw(e_tokenizer, "Tokenizer error");
		return 0;
	}

	if (token->token == type && rstring_equals(token->string_value, value))
	{
		token_release(&token);
		return 1;
	}

	errors_throw(e_token_expected, "Token '%s' expected", value);
	token_release(&token);
	return 0;
}

static inline rstring_t *expect_type(parser_t *self, token_type_t type)
{
	token_t *token = tokenizer_next(self->tokenizer);

	if (!token)
	{
		errors_throw(e_tokenizer, "Tokenizer error");
		return NULL;
	}

	if (token->token != type)
	{
		token_release(&token);
		errors_throw(e_token_expected, "Token type '%s' expected", token_typename(type));
		return NULL;
	}

	rstring_t *value = token->string_value;

	rstring_ref(value);
	token_release(&token);
	return value;
}

static inline char expect_operators(parser_t *self, expr_t *expr, int count, ...)
{
	va_list args;
	token_t *token = tokenizer_peek(self->tokenizer);

	if (!token)
	{
		errors_throw(e_tokenizer, "Tokenizer error");
		return 0;
	}

	if (token->token != tk_operator)
	{
		token_release(&token);
		return 0;
	}

	va_start(args, count);

	for (int i = 0; i < count; i++)
	{
		if (rstring_equals(token->string_value, va_arg(args, const char *)))
		{
			expr->operator = token->string_value;

			rstring_ref(expr->operator);
			token_release(&token);
			return 1;
		}
	}

	token_release(&token);
	return 0;
}

static inline char check_name(parser_t *self, rstring_t *name)
{
	RedObject *key = RedString_FromRString(name);
	RedObject *object = RedMap_GetString(self->consts, key);

	Red_UNREF(key);
	return object ? 0 : 1;
}

/*************** handler prototypes ***************/

/* language structures */

static if_t *parse_if(parser_t *self);
static for_t *parse_for(parser_t *self);
static try_t *parse_try(parser_t *self);
static case_t *parse_case(parser_t *self);
static while_t *parse_while(parser_t *self);
static switch_t *parse_switch(parser_t *self);
static compond_t *parse_compond(parser_t *self);
static function_t *parse_function(parser_t *self);

/* statements */

static assign_t *parse_assign(parser_t *self);
static delete_t *parse_delete(parser_t *self);
static augment_t *parse_augment(parser_t *self);

/* control flows */

static break_t *parse_break(parser_t *self);
static return_t *parse_return(parser_t *self);
static continue_t *parse_continue(parser_t *self);

/* generic statement */

static statement_t *parse_statement(parser_t *self);
static statement_t *parse_simple_statement(parser_t *self);

/* assign targets */

static name_t *parse_name(parser_t *self);
static attr_t *parse_attr(parser_t *self);
static index_t *parse_index(parser_t *self);
static invoke_t *parse_invoke(parser_t *self);
static rvalue_t *parse_rvalue(parser_t *self);

/* expressions */

static expr_t *parse_expr(parser_t *self);
static unit_t *parse_unit(parser_t *self);
static const_t *parse_const(parser_t *self);
static range_t *parse_range(parser_t *self);

/*************** parser handlers ***************/

/* language structures */

static if_t *parse_if(parser_t *self)
{
	def(if, result);
	ensure(keyword,			"if",		if, result);
	verify(result->expr,	expr,		if, result);
	verify(result->pos,		statement,	if, result);

	if (skipif(self, tk_keyword, "else"))
		verify(result->neg, statement, if, result);

	return result;
}

static for_t *parse_for(parser_t *self)
{
	self->nested_loops++;
	def(for, result);
	ensure(keyword,				"for",				for, result, self->nested_loops--);
	verify(result->init,		simple_statement,	for, result, self->nested_loops--);
	verify(result->condition,	expr,				for, result, self->nested_loops--);
	skipif(self, tk_operator, ";");
	verify(result->step,		simple_statement,	for, result, self->nested_loops--);
	verify(result->body,		statement,			for, result, self->nested_loops--);
	self->nested_loops--;
	return result;
}

static try_t *parse_try(parser_t *self)
{
	def(try, result);
	ensure(keyword,			"try",		try, result);
	verify(result->body,	statement,	try, result);
	rarray_create(&(result->excepts), (rarray_free_fn_t)unref_except);

	for (;;)
	{
		token_t *token = tokenizer_peek(self->tokenizer);

		if (!token || token->token != tk_keyword)
		{
			token_release(&token);
			return result;
		}

		if (rstring_equals(token->string_value, "finally"))
		{
			token_release(&token);
			token_unref(tokenizer_next(self->tokenizer));

			if (result->finally)
			{
				unref_try(result);
				errors_throw(e_duplicate_finally, "Duplicated 'finally'");
				return NULL;
			}

			verify(result->finally, statement, try, result);
		}
		else if (rstring_equals(token->string_value, "except"))
		{
			token_release(&token);
			token_unref(tokenizer_next(self->tokenizer));

			if (assume(self, tk_operator, "*"))
			{
				token_unref(tokenizer_next(self->tokenizer));

				if (result->generic)
				{
					unref_try(result);
					errors_throw(e_duplicate_except_all, "Duplicated 'except *'");
					return NULL;
				}

				verify(result->generic, statement, try, result);
			}
			else
			{
				def(except, except);
				rarray_append(&(result->excepts), except);
				rarray_create(&(except->types), (rarray_free_fn_t)rstring_unref);

				rstring_t *type = expect_type(self, tk_identifier);

				if (!type)
				{
					unref_try(result);
					errors_throw(e_identifier_expected, "Exception type name or '*' expected");
					return NULL;
				}

				rarray_append(&(except->types), type);

				while (assume(self, tk_operator, "or"))
				{
					token_unref(tokenizer_next(self->tokenizer));
					rstring_t *type = expect_type(self, tk_identifier);

					if (!type)
					{
						unref_try(result);
						errors_throw(e_identifier_expected, "Exception type name or '*' expected");
						return NULL;
					}

					rarray_append(&(except->types), type);
				}

				if (assume(self, tk_keyword, "as"))
				{
					token_unref(tokenizer_next(self->tokenizer));

					if (!(except->name = expect_type(self, tk_identifier)))
					{
						unref_try(result);
						errors_throw(e_identifier_expected, "Identifier expected");
						return NULL;
					}
				}

				verify(except->body, statement, try, result);
			}
		}
		else
		{
			token_release(&token);
			return result;
		}
	}
}

static case_t *parse_case(parser_t *self)
{
	def(case, result);
	rarray_create(&(result->ranges), (rarray_free_fn_t)unref_range);

	range_t *range;
	verify(range, range, case, result);
	rarray_append(&(result->ranges), range);

	while (assume(self, tk_operator, ","))
	{
		range_t *range;
		token_unref(tokenizer_next(self->tokenizer));

		verify(range, range, case, result);
		rarray_append(&(result->ranges), range);
	}

	ensure(operator, ":", case, result);
	verify(result->body, statement, case, result);
	return result;
}

static while_t *parse_while(parser_t *self)
{
	self->nested_loops++;
	def(while, result);
	ensure(keyword,			"while",	while, result, self->nested_loops--);
	verify(result->expr,	expr,		while, result, self->nested_loops--);
	verify(result->body,	statement,	while, result, self->nested_loops--);
	self->nested_loops--;
	return result;
}

static switch_t *parse_switch(parser_t *self)
{
	def(switch, result);
	rarray_create(&(result->cases), (rarray_free_fn_t)unref_case);

	ensure(keyword,			"switch",	switch, result);
	verify(result->expr,	expr,		switch, result);
	ensure(operator,		"{",		switch, result);

	while (!assume(self, tk_operator, "}"))
	{
		rstring_t *keyword = expect_type(self, tk_keyword);

		if (!keyword)
		{
			unref_switch(result);
			return NULL;
		}

		if (rstring_equals(keyword, "case"))
		{
			case_t *item;
			verify(item, case, switch, result, rstring_release(&keyword));
			rarray_append(&(result->cases), item);
		}
		else if (rstring_equals(keyword, "default"))
		{
			if (result->other)
			{
				errors_throw(e_default_duplicated, "Duplicated 'default' statement");
				unref_switch(result);
				rstring_release(&keyword);
				return NULL;
			}

			ensure(operator, ":", switch, result, rstring_release(&keyword));
			verify(result->other, statement, switch, result, rstring_release(&keyword));
		}
		else
		{
			errors_throw(e_case_default_expected, "'case' or 'default' expected");
			unref_switch(result);
			rstring_release(&keyword);
			return NULL;
		}

		rstring_release(&keyword);
	}

	token_unref(tokenizer_next(self->tokenizer));
	return result;
}

static compond_t *parse_compond(parser_t *self)
{
	def(compond, result);
	rarray_create(&(result->statements), (rarray_free_fn_t)unref_statement);

	ensure(operator, "{", compond, result);

	while (!assume(self, tk_operator, "}"))
	{
		statement_t *statement;
		verify(statement, statement, compond, result);
		rarray_append(&(result->statements), statement);
	}

	token_unref(tokenizer_next(self->tokenizer));
	return result;
}

static function_t *parse_function(parser_t *self)
{
	self->nested_funcs++;
	def(function, result);
	result->vararg = NULL;
	rarray_create(&(result->args), (rarray_free_fn_t)rstring_unref);

	ensure(keyword, "func", function, result, self->nested_funcs--);

	/* anonymous function support */
	if (maybe(self, 1, tk_identifier))
	{
		result->name = expect_type(self, tk_identifier);

		/* function name cannot be built-in constats */
		if (!check_name(self, result->name))
		{
			self->nested_funcs--;
			errors_throw(e_assign_constant, "Assign to constant");
			unref_function(result);
			return NULL;
		}
	}

	if (assume(self, tk_operator, "("))
	{
		token_unref(tokenizer_next(self->tokenizer));

		if (!assume(self, tk_operator, ")"))
		{
			for (;;)
			{
				/* variable-argument support */
				if (assume(self, tk_operator, ">"))
				{
					token_unref(tokenizer_next(self->tokenizer));
					result->vararg = expect_type(self, tk_identifier);

					/* variable-argument name cannot be built-in constats */
					if (!check_name(self, result->vararg))
					{
						self->nested_funcs--;
						errors_throw(e_assign_constant, "Assign to constant");
						unref_function(result);
						return NULL;
					}

					if (assume(self, tk_operator, ")"))
						break;

					self->nested_funcs--;
					errors_throw(e_op_expected, "Operator ')' expected");
					unref_function(result);
					return NULL;
				}

				rstring_t *arg = expect_type(self, tk_identifier);

				if (!arg)
				{
					self->nested_funcs--;
					unref_function(result);
					return NULL;
				}

				/* argument names cannot be built-in constats */
				if (!check_name(self, arg))
				{
					self->nested_funcs--;
					errors_throw(e_assign_constant, "Assign to constant");
					unref_function(result);
					return NULL;
				}

				rarray_append(&(result->args), arg);

				if (assume(self, tk_operator, ")"))
					break;

				ensure(operator, ",", function, result, self->nested_funcs--);
			}
		}

		token_unref(tokenizer_next(self->tokenizer));
	}

	verify(result->body, statement, function, result, self->nested_funcs--);
	self->nested_funcs--;
	return result;
}

/* statements */

static assign_t *parse_assign(parser_t *self)
{
	def(assign, result);
	ensure(operator, "=", assign, result);
	verify(result->expr, expr, assign, result);
	return result;
}

static delete_t *parse_delete(parser_t *self)
{
	rvalue_t *rvalue;
	def(delete, result);
	ensure(keyword, "delete",	delete, result);
	verify(rvalue,	rvalue,		delete, result);

	switch (rvalue->type)
	{
		case rv_attr:
		{
			result->type = tv_attr;
			result->tv_attr = rvalue->rv_attr;
			ref_any(result->tv_attr);
			break;
		}

		case rv_name:
		{
			result->type = tv_name;
			result->tv_name = rvalue->rv_name;
			ref_any(result->tv_name);
			break;
		}

		case rv_index:
		{
			result->type = tv_index;
			result->tv_index = rvalue->rv_index;
			ref_any(result->tv_index);
			break;
		}

		case rv_unit:
		case rv_const:
		case rv_invoke:
		{
			errors_throw(e_illegal_delete, "Illegal delete expression");
			unref_rvalue(rvalue);
			unref_delete(result);
			return NULL;
		}
	}

	unref_rvalue(rvalue);
	skipif(self, tk_operator, ";");
	return result;
}

static augment_t *parse_augment(parser_t *self)
{
	def(augment, result);
	rstring_t *op = expect_type(self, tk_operator);

	if (!op)
	{
		errors_throw(e_tokenizer, "Tokenizer error");
		unref_augment(result);
		return NULL;
	}

	if (!rstring_is_one_of(op, 11, "+=", "-=", "*=", "/=", "%=", "**=", "|=", "&=", "^=", "<<=", ">>="))
	{
		errors_throw(e_invalid_op, "Invalid operator '%s'", op->content);
		unref_augment(result);
		rstring_release(&op);
		return NULL;
	}

	result->operator = op;
	verify(result->expr, expr, augment, result);
	return result;
}

/* control flows */

static break_t *parse_break(parser_t *self)
{
	if (self->nested_loops == 0)
	{
		errors_throw(e_outside_break, "'break' outside loop");
		return NULL;
	}

	def(break, result);
	ensure(keyword, "break", break, result);
	skipif(self, tk_operator, ";");
	return result;
}

static return_t *parse_return(parser_t *self)
{
	if (self->nested_funcs == 0)
	{
		errors_throw(e_outside_break, "'return' outside function");
		return NULL;
	}

	def(return, result);
	ensure(keyword, "return", return, result);
	verify(result->expr, expr, return, result);
	skipif(self, tk_operator, ";");
	return result;
}

static continue_t *parse_continue(parser_t *self)
{
	if (self->nested_loops == 0)
	{
		errors_throw(e_outside_break, "'continue' outside loop");
		return NULL;
	}

	def(continue, result);
	ensure(keyword, "continue", continue, result);
	skipif(self, tk_operator, ";");
	return result;
}

/* generic statement */

static statement_t *parse_statement(parser_t *self)
{
#define match_op(n)					 if (assume(self, tk_operator, "{"))	{ result->type = st_##n; verify(result->st_##n, n, statement, result); }
#define match_keyword(n)		else if (assume(self, tk_keyword,   #n))	{ result->type = st_##n; verify(result->st_##n, n, statement, result); }
#define match_keyword2(n, v, s)	else if (assume(self, tk_keyword,   #v))	{ result->type = st_##n; verify(result->st_##n, n, statement, result); s }
#define match_otherwise()		else										{ unref_statement(result); verify(result, simple_statement, statement, result); }

	def(statement, result);
	match_op(compond)
	match_keyword(if)
	match_keyword(for)
	match_keyword(try)
	match_keyword(while)
	match_keyword(switch)
	match_keyword(break)
	match_keyword(return)
	match_keyword(delete)
	match_keyword(continue)
	match_keyword2(function, func, if (!result->st_function->name)
	{
		errors_throw(e_only_named_func, "Must be named function");
		unref_statement(result);
		return NULL;
	})
	match_otherwise()
	return result;

#undef match_op
#undef match_keyword
#undef match_keyword2
#undef match_otherwise
}

static statement_t *parse_simple_statement(parser_t *self)
{
	rvalue_t *rvalue;
	def(statement, result);
	verify(rvalue, rvalue, statement, result);

	switch (rvalue->type)
	{
		case rv_const:
		{
			errors_throw(e_assign_constant, "Assign to constant");
			unref_statement(result);
			return NULL;
		}

		case rv_invoke:
		{
			result->type = st_invoke;
			result->st_invoke = rvalue->rv_invoke;
			result->st_invoke->discard = 1;
			ref_any(result->st_invoke);
			unref_rvalue(rvalue);
			skipif(self, tk_operator, ";");
			return result;
		}

#define rvalue_convert(s, t)						\
	case rv_##t:									\
	{												\
		result->st_##s->type = tv_##t;				\
		result->st_##s->tv_##t = rvalue->rv_##t;	\
		ref_any(result->st_##s->tv_##t);			\
		break;										\
	}

#define do_parse_statement(s)															\
	{																					\
		result->type = st_##s;															\
		verify(result->st_##s, s, statement, result, unref_rvalue(rvalue));				\
																						\
		switch (rvalue->type)															\
		{																				\
			rvalue_convert(s, attr)														\
			rvalue_convert(s, name)														\
			rvalue_convert(s, index)													\
																						\
			default:																	\
			{																			\
				fprintf(stderr, "Internal error (impossible rvalue->type)");			\
				abort();																\
			}																			\
		}																				\
	}

		default:
		{
			/* validate assign target */
			if (rvalue->type == rv_name && !check_name(self, rvalue->rv_name->name))
			{
				errors_throw(e_assign_constant, "Assign to constant");
				unref_statement(result);
				return NULL;
			}

			if (assume(self, tk_operator, "="))
				do_parse_statement(assign)
			else
				do_parse_statement(augment)

			break;
		}

#undef rvalue_convert
#undef do_parse_statement
	}

	unref_rvalue(rvalue);
	skipif(self, tk_operator, ";");
	return result;
}

/* assign targets */

static name_t *parse_name(parser_t *self)
{
	def(name, result);

	if (!(result->name = expect_type(self, tk_identifier)))
	{
		unref_name(result);
		return NULL;
	}

	return result;
}

static attr_t *parse_attr(parser_t *self)
{
	def(attr, result);
	ensure(operator, ".", attr, result);

	if (!(result->attribute = expect_type(self, tk_identifier)))
	{
		unref_attr(result);
		return NULL;
	}

	return result;
}

static index_t *parse_index(parser_t *self)
{
	def(index, result);
	ensure(operator,		"[",	index, result);
	verify(result->expr,	expr,	index, result);
	ensure(operator,		"]",	index, result);
	return result;
}

static invoke_t *parse_invoke(parser_t *self)
{
	def(invoke, result);
	rarray_create(&(result->args), (rarray_free_fn_t)unref_expr);
	rarray_create(&(result->expand), NULL);

	ensure(operator, "(", invoke, result);

	if (assume(self, tk_operator, ")"))
	{
		token_unref(tokenizer_next(self->tokenizer));
		return result;
	}

	for (;;)
	{
		intptr_t expand = 0;

		if (assume(self, tk_operator, ">"))
		{
			expand = 1;
			result->variable = 1;
			token_unref(tokenizer_next(self->tokenizer));
		}

		expr_t *expr;
		verify(expr, expr, invoke, result);

		rarray_append(&(result->args), expr);
		rarray_append(&(result->expand), (void *)expand);

		if (assume(self, tk_operator, ")"))
		{
			token_unref(tokenizer_next(self->tokenizer));
			return result;
		}

		ensure(operator, ",", invoke, result);
	}
}

static rvalue_t *parse_rvalue(parser_t *self)
{
	def(rvalue, result);
	token_t *token = tokenizer_peek(self->tokenizer);

	if (!token)
	{
		errors_throw(e_tokenizer, "Tokenizer error");
		return NULL;
	}

	switch (token->token)
	{
		case tk_keyword:
		case tk_operator:
		{
			result->type = rv_unit;
			verify(result->rv_unit, unit, rvalue, result);
			token_release(&token);
			break;
		}

		case tk_identifier:
		{
			result->type = rv_name;
			verify(result->rv_name, name, rvalue, result);
			token_release(&token);

			/* syntactic suger of binary tuple, wrap it as `invoke` */
			if (assume(self, tk_operator, "=>"))
			{
				token_unref(tokenizer_next(self->tokenizer));

				def(name, fname);
				fname->name = rstring_new_string("tuple");

				def(rvalue, value);
				value->type = rv_name;
				value->rv_name = fname;

				def(invoke, tuple);
				tuple->value = value;
				rarray_create(&(tuple->args), (rarray_free_fn_t)unref_expr);
				rarray_create(&(tuple->expand), NULL);

				def(const, arg0c);
				arg0c->type = c_string;
				arg0c->c_string = result->rv_name->name;
				rstring_ref(result->rv_name->name);
				unref_name(result->rv_name);

				def(rvalue, arg0v);
				arg0v->type = rv_const;
				arg0v->rv_const = arg0c;

				def(expr, arg0);
				arg0->el_type = e_rvalue;
				arg0->el_rvalue = arg0v;

				result->type = rv_invoke;
				result->rv_invoke = tuple;

				rarray_append(&(tuple->args), arg0);
				rarray_append(&(tuple->expand), NULL);

				def(expr, arg1);
				verify(arg1, expr, rvalue, result);

				rarray_append(&(tuple->args), arg1);
				rarray_append(&(tuple->expand), NULL);
			}

			break;
		}

		case tk_float:
		case tk_string:
		case tk_integer:
		{
			result->type = rv_const;
			verify(result->rv_const, const, rvalue, result);
			token_release(&token);
			break;
		}
	}

	for (;;)
	{
		token_t *token = tokenizer_peek(self->tokenizer);

		if (!token || (token->token != tk_operator))
		{
			token_release(&token);
			return result;
		}

		if (rstring_equals(token->string_value, "."))
		{
			def(rvalue, value);
			token_release(&token);
			value->type = rv_attr;
			verify(value->rv_attr, attr, rvalue, result, unref_rvalue(value));
			value->rv_attr->value = result;
			result = value;
		}
		else if (rstring_equals(token->string_value, "["))
		{
			def(rvalue, value);
			token_release(&token);
			value->type = rv_index;
			verify(value->rv_index, index, rvalue, result, unref_rvalue(value));
			value->rv_index->value = result;
			result = value;
		}
		else if (rstring_equals(token->string_value, "("))
		{
			def(rvalue, value);
			token_release(&token);
			value->type = rv_invoke;
			verify(value->rv_invoke, invoke, rvalue, result, unref_rvalue(value));
			value->rv_invoke->value = result;
			result = value;
		}
		else
		{
			token_release(&token);
			return result;
		}
	}
}

/* expressions */

#define do_parse_expressions(t, p, n, ...)							\
{																	\
	def(expr, result);												\
	result->el_type = e_##t;										\
	result->er_type = e_##t;										\
	verify(result->el_##t, p, expr, result);						\
																	\
	if (expect_operators(self, result, n, __VA_ARGS__))				\
	{																\
		def(expr, node);											\
		node->el_type = e_expr;										\
		node->er_type = e_##t;										\
		token_unref(tokenizer_next(self->tokenizer));				\
		verify(result->er_##t, p, expr, result);					\
																	\
		while (expect_operators(self, node, n, __VA_ARGS__))		\
		{															\
			node->el_expr = result;									\
			token_unref(tokenizer_next(self->tokenizer));			\
			verify(node->er_##t, p, expr, node);					\
			result = node;											\
			create(expr, node);										\
		}															\
																	\
		unref_expr(node);											\
	}																\
																	\
	return result;													\
}

static expr_t *parse_power(parser_t *self)			do_parse_expressions(rvalue,	rvalue		, 1, "**")
static expr_t *parse_factor(parser_t *self)			do_parse_expressions(expr,		power		, 3, "*", "/", "%")
static expr_t *parse_term(parser_t *self)			do_parse_expressions(expr,		factor		, 2, "+", "-")
static expr_t *parse_bitwise(parser_t *self)		do_parse_expressions(expr,		term		, 5, "|", "&", "^", "<<", ">>")
static expr_t *parse_relations(parser_t *self)		do_parse_expressions(expr,		bitwise		, 6, "<", ">", "<=", ">=", "==", "!=")
static expr_t *parse_expr(parser_t *self)			do_parse_expressions(expr,		relations	, 3, "and", "or", "xor")

#undef do_parse_expressions

static unit_t *parse_unit(parser_t *self)
{
	def(unit, result);
	token_t *token = tokenizer_peek(self->tokenizer);

	if (!token)
	{
		unref_unit(result);
		errors_throw(e_tokenizer, "Tokenizer error");
		return NULL;
	}

	switch (token->token)
	{
		case tk_keyword:
		{
			if (!rstring_equals(token->string_value, "func"))
			{
				unref_unit(result);
				errors_throw(e_keyword_unexpected, "Unexpected keyword '%s'", token->string_value->content);
				token_release(&token);
				return NULL;
			}

			result->type = u_function;
			verify(result->u_function, function, unit, result, token_release(&token));

			if (result->u_function->name)
			{
				unref_unit(result);
				errors_throw(e_only_anonymous_func, "Must be anonymouse function");
				token_release(&token);
				return NULL;
			}

			break;
		}

		/* includes syntactic-suger implementation of array literals / mapping literals and tuple literals */
		case tk_operator:
		{
			token_unref(tokenizer_next(self->tokenizer));

			if (rstring_equals(token->string_value, "["))
			{
				/* array literals, wrap it as `invoke` */
				def(name, fname);
				fname->name = rstring_new_string("array");

				def(rvalue, function);
				function->type = rv_name;
				function->rv_name = fname;

				def(invoke, array);
				array->value = function;
				rarray_create(&(array->args), (rarray_free_fn_t)unref_expr);
				rarray_create(&(array->expand), NULL);

				def(rvalue, value);
				value->type = rv_invoke;
				value->rv_invoke = array;

				result->type = u_rvalue;
				result->u_rvalue = value;

				while (!assume(self, tk_operator, "]"))
				{
					expr_t *expr;
					verify(expr, expr, unit, result, token_release(&token));
					rarray_append(&(array->args), expr);
					rarray_append(&(array->expand), NULL);

					if (!assume(self, tk_operator, "]"))
						ensure(operator, ",", unit, result, token_release(&token));
				}

				token_unref(tokenizer_next(self->tokenizer));
			}
			else if (rstring_equals(token->string_value, "{"))
			{
				/* mapping literals, wrap it as `invoke` */
				def(name, fname);
				fname->name = rstring_new_string("map");

				def(rvalue, function);
				function->type = rv_name;
				function->rv_name = fname;

				def(invoke, map);
				map->value = function;
				rarray_create(&(map->args), (rarray_free_fn_t)unref_expr);
				rarray_create(&(map->expand), NULL);

				def(rvalue, value);
				value->type = rv_invoke;
				value->rv_invoke = map;

				result->type = u_rvalue;
				result->u_rvalue = value;

				while (!assume(self, tk_operator, "}"))
				{
					def(name, name);
					name->name = rstring_new_string("tuple");

					def(rvalue, func);
					func->type = rv_name;
					func->rv_name = name;

					def(invoke, invoke);
					invoke->value = func;
					rarray_create(&(invoke->args), (rarray_free_fn_t)unref_expr);
					rarray_create(&(invoke->expand), NULL);

					def(rvalue, rvalue);
					rvalue->type = rv_invoke;
					rvalue->rv_invoke = invoke;

					def(expr, expr);
					expr->el_type = e_rvalue;
					expr->el_rvalue = rvalue;
					rarray_append(&(map->args), expr);
					rarray_append(&(map->expand), NULL);

					def(expr, key);
					verify(key, expr, unit, result, token_release(&token));
					rarray_append(&(invoke->args), key);
					rarray_append(&(invoke->expand), NULL);

					ensure(operator, ":", unit, result, token_release(&token));

					def(expr, value);
					verify(value, expr, unit, result, token_release(&token));
					rarray_append(&(invoke->args), value);
					rarray_append(&(invoke->expand), NULL);

					if (!assume(self, tk_operator, "}"))
						ensure(operator, ",", unit, result, token_release(&token));
				}

				token_unref(tokenizer_next(self->tokenizer));
			}
			else if (rstring_equals(token->string_value, "("))
			{
				/* empty tuple literals */
				if (assume(self, tk_operator, ")"))
				{
					def(name, fname);
					fname->name = rstring_new_string("tuple");

					def(rvalue, function);
					function->type = rv_name;
					function->rv_name = fname;

					def(invoke, tuple);
					tuple->value = function;
					rarray_create(&(tuple->args), (rarray_free_fn_t)unref_expr);
					rarray_create(&(tuple->expand), NULL);

					def(rvalue, value);
					value->type = rv_invoke;
					value->rv_invoke = tuple;

					result->type = u_rvalue;
					result->u_rvalue = value;

					token_unref(tokenizer_next(self->tokenizer));
					break;
				}

				expr_t *expr;
				verify(expr, expr, unit, result, token_release(&token));

				token_t *next = tokenizer_next(self->tokenizer);

				if (next->token != tk_operator)
				{
					unref_unit(result);
					errors_throw(e_op_expected, "Operator expected");
					token_release(&next);
					token_release(&token);
					return NULL;
				}

				/* nested expression */
				if (rstring_equals(next->string_value, ")"))
				{
					result->type = u_expr;
					result->u_expr = expr;
					token_release(&next);
					break;
				}

				/* tuple literals, wrap it as `invoke` */
				if (rstring_equals(next->string_value, ","))
				{
					def(name, fname);
					fname->name = rstring_new_string("tuple");

					def(rvalue, function);
					function->type = rv_name;
					function->rv_name = fname;

					def(invoke, tuple);
					tuple->value = function;
					rarray_create(&(tuple->args), (rarray_free_fn_t)unref_expr);
					rarray_create(&(tuple->expand), NULL);

					def(rvalue, value);
					value->type = rv_invoke;
					value->rv_invoke = tuple;

					result->type = u_rvalue;
					result->u_rvalue = value;

					token_release(&next);
					rarray_append(&(tuple->args), expr);

					while (!assume(self, tk_operator, ")"))
					{
						verify(expr, expr, unit, result, token_release(&token));
						rarray_append(&(tuple->args), expr);
						rarray_append(&(tuple->expand), NULL);

						if (!assume(self, tk_operator, ")"))
							ensure(operator, ",", unit, result, token_release(&token));
					}

					token_unref(tokenizer_next(self->tokenizer));
					break;
				}

				unref_unit(result);
				errors_throw(e_invalid_op, "Invalid operator '%s'", next->string_value->content);
				token_release(&next);
				token_release(&token);
				return NULL;
			}
			else if (rstring_is_one_of(token->string_value, 4, "+", "-", "~", "not"))
			{
				result->type = u_unit;
				result->operator = token->string_value;
				rstring_ref(result->operator);
				verify(result->u_unit, unit, unit, result, token_release(&token));
			}
			else
			{
				unref_unit(result);
				errors_throw(e_invalid_op, "Invalid operator '%s'", token->string_value->content);
				token_release(&token);
				return NULL;
			}

			break;
		}

		case tk_float:
		case tk_string:
		case tk_integer:
		case tk_identifier:
		{
			result->type = u_rvalue;
			verify(result->u_rvalue, rvalue, unit, result, token_release(&token));
			break;
		}
	}

	token_release(&token);
	return result;
}

static const_t *parse_const(parser_t *self)
{
	def(const, result);
	token_t *token = tokenizer_next(self->tokenizer);

	if (!token)
	{
		unref_const(result);
		errors_throw(e_tokenizer, "Tokenizer error");
		return NULL;
	}

	switch (token->token)
	{
		case tk_float:
		{
			result->type = c_float;
			result->c_float = token->float_value;
			break;
		}

		case tk_string:
		{
			result->type = c_string;
			result->c_string = token->string_value;
			rstring_ref(result->c_string);
			break;
		}

		case tk_integer:
		{
			result->type = c_int;
			result->c_int = token->int_value;
			break;
		}

		default:
		{
			fprintf(stderr, "Internal error (parse_const()/token->token is not constant type)");
			token_release(&token);
			abort();
		}
	}

	token_release(&token);
	return result;
}

static range_t *parse_range(parser_t *self)
{
	def(range, result);
	verify(result->begin, expr, range, result);

	if (assume(self, tk_operator, ".."))
	{
		token_unref(tokenizer_next(self->tokenizer));
		verify(result->end, expr, range, result);
	}

	return result;
}

/* parser types and functions */

void parser_free(parser_t *self)
{
	Red_UNREF(self->consts);
	tokenizer_free(self->tokenizer);
}

void parser_init(parser_t *self, RedObject *consts, tokenizer_t *tokenizer)
{
	Red_REF(consts);
	self->consts = consts;
	self->tokenizer = tokenizer;
	self->nested_funcs = self->nested_loops = 0;
}

compond_t *parser_parse(parser_t *self)
{
	token_t *token;

	def(compond, result);
	self->nested_funcs = 0;
	self->nested_loops = 0;
	rarray_create(&(result->statements), (rarray_free_fn_t)unref_statement);

	while ((token = tokenizer_peek(self->tokenizer)))
	{
		token_unref(token);
		statement_t *statement;
		verify(statement, statement, compond, result);
		rarray_append(&(result->statements), statement);
	}

	return result;
}
